﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Nested Types
    /// </summary>
    public class Chaincode
    {
        // Fields
        public int nArea;
        public string sCracks;
        public int nPerimeter;
        public Point Start;

        // Methods
        public Chaincode()
        {
        }
    }

    /// <summary>
    /// This implementation will only work with gray level pics
    /// </summary>
    public class ChainCodeFilter : BaseImageFilter
    {
        private int dx = 1;
        private int dy = 1;

        protected bool m_bCodeWith0Zells = true;
        private List<Chaincode> all_chaincodes = new List<Chaincode>();
        protected byte[,] c2 = null;
        protected byte[,] c0 = null;
        protected byte[,] c1v = null;
        protected byte[,] c1h = null;
        protected byte C0, C1V, C1H, C2;
        protected byte m_nThreshold = 1;
        protected Point start = Point.Empty;
        private Pen pinkpen = new Pen(Color.FromArgb(255, 128, 128), 3);
        private Pen redpen = new Pen(Color.Red, 5);
        private bool NullCells;

        private int x;
        private int y;
        int xSize = 0;
        int ySize = 0;

        /// <summary>
        /// Initializes a new instance of the <see cref="ChainCode"/> class.
        /// </summary>
        public ChainCodeFilter()
        {
            this.redpen.EndCap = LineCap.ArrowAnchor;
            this.pinkpen.EndCap = LineCap.ArrowAnchor;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChainCode"/> class.
        /// </summary>
        /// <param name="_chainCode">The _chain code.</param>
        internal ChainCodeFilter(ChainCodeFilter _chainCode)
        {
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            ySize = _bitmap.Height;
            xSize = _bitmap.Width;
            c1v = new byte[ySize, xSize + 1];

            this.all_chaincodes.Clear();

            if (m_bCodeWith0Zells)
            {
                this.NullCells = m_bCodeWith0Zells;
            }
            this.Chaincode_Finder(_bitmap);

            return _bitmap;
        }

        /// <summary>
        /// Start_crack_searches the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <returns></returns>
        private bool Start_Crack_Search(UnsafeBitmap _bitmap)
        {
            this.y = this.start.Y;
            while (this.y < 12)
            {
                this.x = 0;
                while (this.x < 11)
                {
                    byte num;
                    if (this.x > 0)
                    {
                        num = _bitmap[this.x - 1, this.y, true].R;
                    }
                    else
                    {
                        num = 0;
                    }
                    if (((num < this.m_nThreshold) && (_bitmap[this.x, this.y, true].R >= this.m_nThreshold)) && (this.c1v[this.y, this.x] == 0))
                    {
                        this.start.X = this.x;
                        this.start.Y = this.y;
                        this.c1v[this.y, this.x] = 1;
                        return true;
                    }
                    this.x++;
                }
                this.y++;
            }
            return false;
        }

        /// <summary>
        /// Chaincode_finders the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        protected void Chaincode_Finder(UnsafeBitmap _bitmap)
        {
            sbyte[,] numArray = new sbyte[,] { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } };
            sbyte[,] numArray2 = new sbyte[,] { { 0, 0 }, { -1, 0 }, { -1, -1 }, { 0, -1 } };
            Point point = new Point();
            Point point2 = new Point();
            this.y = 0;
            while (this.y < 12)
            {
                this.x = 0;
                while (this.x <= 11)
                {
                    this.c1v[this.y, this.x] = 0;
                    this.x++;
                }
                this.y++;
            }
            this.start.X = this.start.Y = 0;
            for (byte i = 1; this.Start_Crack_Search(_bitmap); i = (byte)(i + 1))
            {
                Chaincode chaincode = new Chaincode();
                StringBuilder builder = new StringBuilder();
                builder.Append('s');
                chaincode.nArea = 0;
                this.x = this.start.X;
                this.y = this.start.Y + 1;
                int num = 1;
                Graphics graphics = null;//base.CreateGraphics();
                do
                {
                    bool flag;
                    bool flag2;
                    _bitmap.UnlockImage();
                    graphics = Graphics.FromImage(_bitmap.InternalBitmap);
                    graphics.DrawLine(this.pinkpen, (int)(this.x * this.dx), (int)(this.y * this.dy), (int)((this.x * this.dx) + (this.dx / 4)), (int)(this.y * this.dy));
                    graphics.DrawLine(this.pinkpen, (int)(this.x * this.dx), (int)(this.y * this.dy), (int)(this.x * this.dx), (int)((this.y * this.dy) + (this.dy / 4)));
                    graphics.DrawLine(this.pinkpen, (int)(this.x * this.dx), (int)(this.y * this.dy), (int)((this.x * this.dx) - (this.dx / 4)), (int)(this.y * this.dy));
                    graphics.DrawLine(this.pinkpen, (int)(this.x * this.dx), (int)(this.y * this.dy), (int)(this.x * this.dx), (int)((this.y * this.dy) - (this.dy / 4)));
                    switch (builder[builder.Length - 1])
                    {
                        case 's':
                            graphics.DrawLine(this.redpen, (int)(this.x * this.dx), (int)((this.y - 1) * this.dy), (int)(this.x * this.dx), (int)(this.y * this.dy));
                            break;

                        case 'w':
                            graphics.DrawLine(this.redpen, (int)((this.x + 1) * this.dx), (int)(this.y * this.dy), (int)(this.x * this.dx), (int)(this.y * this.dy));
                            break;

                        case 'e':
                            graphics.DrawLine(this.redpen, (int)((this.x - 1) * this.dx), (int)(this.y * this.dy), (int)(this.x * this.dx), (int)(this.y * this.dy));
                            break;

                        case 'n':
                            graphics.DrawLine(this.redpen, (int)(this.x * this.dx), (int)((this.y + 1) * this.dy), (int)(this.x * this.dx), (int)(this.y * this.dy));
                            break;
                    }
                    _bitmap.LockImage();
                    long num3 = DateTime.Now.Ticks + 0x1e8480L;
                    while (num3 > DateTime.Now.Ticks)
                    {
                    }
                    point.X = this.x + numArray[num, 0];
                    point.Y = this.y + numArray[num, 1];
                    point2.X = this.x + numArray2[num, 0];
                    point2.Y = this.y + numArray2[num, 1];
                    try
                    {
                        flag = _bitmap[point.X, point.Y, true].R >= this.m_nThreshold;
                    }
                    catch
                    {
                        flag = false;
                    }
                    try
                    {
                        flag2 = _bitmap[point2.X, point2.Y, true].R >= this.m_nThreshold;
                    }
                    catch
                    {
                        flag2 = false;
                    }
                    if (flag2 && (flag || this.NullCells))
                    {
                        num = (num + 1) % 4;
                    }
                    else if (!flag && (!flag2 || !this.NullCells))
                    {
                        num = (num + 3) % 4;
                    }
                    switch (num)
                    {
                        case 0:
                            builder.Append('e');
                            this.x++;
                            chaincode.nArea += this.y;
                            break;

                        case 1:
                            builder.Append('s');
                            this.y++;
                            this.c1v[this.y - 1, this.x] = i;
                            break;

                        case 2:
                            builder.Append('w');
                            this.x--;
                            chaincode.nArea -= this.y;
                            break;

                        case 3:
                            builder.Append('n');
                            this.y--;
                            this.c1v[this.y, this.x] = i;
                            break;
                    }
                }
                while ((this.x != this.start.X) || (this.y != this.start.Y));
                switch (builder[builder.Length - 1])
                {
                    case 'e':
                        graphics.DrawLine(this.redpen, (int)((this.x - 1) * this.dx), (int)(this.y * this.dy), (int)(this.x * this.dx), (int)(this.y * this.dy));
                        break;

                    case 'w':
                        graphics.DrawLine(this.redpen, (int)((this.x + 1) * this.dx), (int)(this.y * this.dy), (int)(this.x * this.dx), (int)(this.y * this.dy));
                        break;
                }
                chaincode.sCracks = builder.ToString();
                chaincode.nPerimeter = chaincode.sCracks.Length;
                this.all_chaincodes.Add(chaincode);
            }
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ChainCodeFilter(this);
        }
    }
}
